import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

/**
 * 文件已提前读取完毕，如需要读取新表达式文件，
 * 请将文件拖放至文件夹并更新源码
 * @Joey.Xzy
 */
public class Analyze {
    //记号表
    private static HashMap<String, Integer> tokenTable = new HashMap<>();
    //输入缓冲
    private static String in = new String();
    //输出
    private static String out = new String();
    //符号表
    private static Vector<Integer> classes = new Vector<>();
    private static Vector<String> names = new Vector<>();
    private static Vector<String> values = new Vector<>();

    //文件读取
    private static void readFile() {
        FileReader reader = null;
        String root = System.getProperty("user.dir").replace("\\","\\\\");
        try {
            reader = new FileReader(root + "\\src\\test.txt");//读取当前文件夹下的test文件（提前已准备好）
            int data = -1;
            while ((data = reader.read())!= -1) {
                Character str = (char) data;
                if (str!=13) {
                    in += str;
                }
            }
        } catch (FileNotFoundException e) {//异常处理
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //判断是否为数字
    private static int judgeNum(int currentIndex) {
        //构造状态转移表
        char[][] table = new char[7][12];
        int i;
        table[0][0] = 'C';
        table[0][1] = 'D';
        table[0][10] = 'B';
        table[1][0] = 'C';
        table[1][1] = 'D';
        table[2][11] = 'E';
        table[3][0] = 'D';
        table[3][1] = 'D';
        table[3][11] = 'E';
        table[4][0] = 'G';
        table[4][1] = 'F';
        table[5][0] = 'G';
        table[5][1] = 'F';
        table[6][1] = 'G';
        for(i=0;i<7;i++){
            for(int j = 2;j<=9;j++){
                table[i][j] = table[i][j-1];
            }
        }
        //字符集
        Hashtable<Character,Integer> charSet = new Hashtable<>();
        charSet.put('-',10);
        charSet.put('.',11);
        for (i=0;i<10;i++){
            charSet.put((char)(i+'0'),i);
        }
        //状态集
        Hashtable<Character,Integer> stateSet = new Hashtable<>();
        stateSet.put('A',0);
        stateSet.put('B',1);
        stateSet.put('C',2);
        stateSet.put('D',3);
        stateSet.put('E',4);
        stateSet.put('F',5);
        stateSet.put('G',6);
        stateSet.put('H',7);
        //初态
        char currentState = 'A';
        //终态集
        ArrayList<Character> finallyState = new ArrayList<>(Arrays.asList('C','D','F'));
        char currentChar = in.charAt(currentIndex);
        if ((currentChar < '0' || currentChar > '9' ) && currentChar != '-'){
            return -1;
        }
        //状态转移函数
        while(charSet.containsKey(currentChar)) {
            currentState = table[stateSet.get(currentState)][charSet.get(currentChar)];
            if (currentState == '\0') {
                return currentIndex;
            }
            currentIndex ++;
            currentChar = in.charAt(currentIndex);
        }
        //判断是否为终态
        if (finallyState.contains(currentState)) {
            //返回下一个字符的起点
            return currentIndex;
        }
        return -1;
    }

    //判断是否为标识符
    private static int judgeVar(int currentIndex) {

        ArrayList<Character> charSet = new ArrayList<>(Arrays.asList('_'));
        int i ;
        for (i = 'a';i <= 'z'; i ++) {
            charSet.add((char) i);
        }
        for (i = 'A';i <= 'Z'; i++) {
            charSet.add((char) i );
        }
        char currentChar = in.charAt(currentIndex);
        if (!charSet.contains(currentChar)) {
            return -1;
        }
        for(i='0';i<='9';i++) {
            charSet.add((char)i);
        }
        //状态转移函数
        char currentState = 'B';
        while(charSet.contains(currentChar)) {
            currentIndex++;
            currentChar = in.charAt(currentIndex);
        }
        return currentIndex;
    }


    //判断是否为初等函数
    private static boolean judgeFunction(String subString) {
        switch (subString){
            case "sin" :
            case "cos" :
            case "tg"  :
            case "ctg" :
            case "log" :
            case "lg" :
            case "ln" : return true;
        }
        return false;
    }

    //判断是否为运算符
    private static int judgeOperator(int currentIndex) {
        char currentChar = in.charAt(currentIndex);
        switch (currentChar) {
            case '+':
            case '-':
            case '*':
            case '/':
            case '=':
            case '^': return currentIndex+1;
        }
        return -1;
    }

    //判断是否为分隔符
    private static int judgeSeparator(int currentIndex) {
        char currentChar = in.charAt(currentIndex);
        switch (currentChar) {
            case ' ' :
            case '\n' :
            case '\t' :
            case '(' :
            case ')' :
            case ';' :
            case ',' : return currentIndex+1;
        }
        return -1;
    }

    //判断是否为输出符
    private static int judgeOut(int currentIndex) {
        if (in.charAt(currentIndex) == '?') {
            return currentIndex+1;
        }
        return -1;
    }

    //判断是否为枚举字面量
    private static boolean judgeEnumerator(String subString) {
        switch (subString) {
            case "PI" :
            case "E" : return true;
        }
        return false;
    }

    //词法分析器
    private static boolean Analyse() {
        int currentIndex = 0;
        //长度
        int bufferLength = in.length();
        //添加终结符
        in += '\0';
        //下一个字符的起点
        int nextStartIndex = 0;
        String temp;
        int num = 0;
        Hashtable<String , String> varTable = new Hashtable<>();
        while (currentIndex < bufferLength) {
            //是否为标识符
            nextStartIndex = judgeVar(currentIndex);
            if (nextStartIndex != -1) {
                temp = in.substring(currentIndex,nextStartIndex);
                //是否为关键字
                if (judgeFunction(temp)){
                    classes.add(tokenTable.get(temp));
                    names.add(temp);
                    values.add(temp);
                    //是否为枚举字面量
                } else if (judgeEnumerator(temp)) {
                    classes.add(tokenTable.get(temp));
                    names.add(temp);
                    values.add(temp);
                    //就是标识符了
                } else {
                    classes.add(tokenTable.get("variable"));
                    if (!varTable.containsKey(temp)) {
                        num++;
                        varTable.put(temp,"id" + String.valueOf(num));
                    }
                    names.add(varTable.get(temp));
                    values.add(temp);
                }
                currentIndex = nextStartIndex;
                continue;
            }
            //是否为数值字面量
            nextStartIndex = judgeNum(currentIndex);
            if (nextStartIndex != -1) {
                temp = in.substring(currentIndex,nextStartIndex);
                classes.add(tokenTable.get("num"));
                names.add(temp);
                values.add(temp);
                currentIndex = nextStartIndex;
                continue;
            }
            //是否为分隔符
            nextStartIndex = judgeSeparator(currentIndex);
            if (nextStartIndex != -1) {
                temp = in.substring(currentIndex,nextStartIndex);
                classes.add(tokenTable.get(temp));
                names.add(temp);
                values.add(temp);
                currentIndex = nextStartIndex;
                continue;
            }

            //是否为运算符
            nextStartIndex = judgeOperator(currentIndex);
            if (nextStartIndex != -1) {
                temp = in.substring(currentIndex,nextStartIndex);
                classes.add(tokenTable.get(temp));
                names.add(temp);
                values.add(temp);
                currentIndex = nextStartIndex;
                continue;
            }

            //是否为输出符
            nextStartIndex = judgeOut(currentIndex);
            if (nextStartIndex != -1) {
                temp = in.substring(currentIndex,nextStartIndex);
                classes.add(tokenTable.get(temp));
                names.add(temp);
                values.add(temp);
                currentIndex = nextStartIndex;
                continue;
            }
            break;
        }

        if (currentIndex == bufferLength) {
            return true;
        }
        System.out.print(in);
        System.out.println("word illegal at "+ String.valueOf(currentIndex) + " " + in.charAt(currentIndex));
        return false;
    }

    //结果展示
    private static void showCharTable() {
        int i;
        System.out.println(" 类别 " + "  记号 " + "  属性");
        for(i=0;i<classes.size();i++) {
            int c = classes.get(i);
            String n = names.get(i);
            if (n.equals("\n")) {
                n = "\\n";
            } else if (n.equals(" ")){
                n = "blank";
            }
            String v = values.get(i);
            if (v.equals("\n")) {
                v = "\\n";
            } else if (v.equals(" ")){
                v = "blank";
            }
            System.out.printf("%4s",c);
            System.out.printf("%6s",n);
            System.out.printf("%6s",v);
            System.out.println();
        }
    }


    //主函数
    public static void main(String[] args) {
        //记号表构造
        tokenTable.put("PI",1);
        tokenTable.put("E",2);
        tokenTable.put("num",3);
        tokenTable.put("variable",4);
        tokenTable.put("sin",5);
        tokenTable.put("cos",6);
        tokenTable.put("tg",7);
        tokenTable.put("ctg",8);
        tokenTable.put("log",9);
        tokenTable.put("lg",10);
        tokenTable.put("ln",11);
        tokenTable.put("+",12);
        tokenTable.put("-",13);
        tokenTable.put("*",14);
        tokenTable.put("/",15);
        tokenTable.put("=",16);
        tokenTable.put("^",17);
        tokenTable.put(" ",18);
        tokenTable.put("\n",19);
        tokenTable.put("\t",20);
        tokenTable.put("(",21);
        tokenTable.put(")",22);
        tokenTable.put(";",23);
        tokenTable.put(",",24);
        tokenTable.put("?",25);

        //文件读取
        readFile();
        System.out.println(in);
        if(Analyse()) {
            for(int i=0;i<names.size();i++){
                out += (names.get(i));
            }
            System.out.println("----------------------------------------------------------------");
            System.out.println("表达式:\n" + out);
            System.out.println("----------------------------------------------------------------");
            System.out.println("符号表:");
            System.out.println();
            showCharTable();
        }
    }

}
